package com.bango.blackberry;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Date;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;

import javax.microedition.io.Connector;
import javax.microedition.io.HttpConnection;
import javax.microedition.io.StreamConnection;
import javax.microedition.location.Criteria;
import javax.microedition.location.Location;
import javax.microedition.location.LocationException;
import javax.microedition.location.LocationListener;
import javax.microedition.location.LocationProvider;

import net.rim.device.api.io.http.HttpProtocolConstants;
import net.rim.device.api.system.ApplicationDescriptor;
import net.rim.device.api.system.ControlledAccessException;
import net.rim.device.api.system.DeviceInfo;
import net.rim.device.api.system.PersistentObject;
import net.rim.device.api.system.PersistentStore;
import net.rim.device.api.system.RadioInfo;
import net.rim.device.api.system.WLANInfo;

import org.json.me.JSONArray;
import org.json.me.JSONObject;

public class BangoLogger implements LocationListener
{

	private PersistentObject _store;
	private BangoStores _bangoStores;

	private LocationProvider _locationProvider;

	private String _applicationId;
	private String _userAgent;
	private String _userId;

	private BangoProfile _bangoProfile;
	private Vector _bangoEvents;

	private boolean _logEvents = BangoConstants.SETTING_LOGEVENTS;
	private int _sessionResumePeriod = BangoConstants.SETTING_SESSION_RESUME_PERIOD;
	private String _postURL = BangoConstants.SETTING_POST_URL;
	private String _locationSource = BangoConstants.SETTING_LOCATION_SOURCE;
	private boolean _findLocation = BangoConstants.SETTING_LOCATION_FIND;
	private String _applicationType = "";
	private String _applicationChannel = "";
	private String _applicationVersion;

	public BangoProfile Profile()
	{
		return _bangoProfile;
	}

	public Vector Events()
	{
		return _bangoEvents;
	}

	public void LogEvents(boolean logEvents)
	{
		_logEvents = logEvents;
	}

	public boolean LogEvents()
	{
		return _logEvents;
	}

	public void SessionResumePeriod(int sessionResumePeriod)
	{
		_sessionResumePeriod = sessionResumePeriod;
	}

	public int SessionResumePeriod()
	{
		return _sessionResumePeriod;
	}

	public void ApplicationVersion(String applicationVersion)
	{
		_applicationVersion = applicationVersion;
	}

	public String ApplicationVersion()
	{
		return _applicationVersion;
	}

	public void ApplicationType(String applicationType)
	{
		_applicationType = applicationType;
	}

	public String ApplicationType()
	{
		return _applicationType;
	}

	public void ApplicationChannel(String applicationChannel)
	{
		_applicationChannel = applicationChannel;
	}

	public String ApplicationChannel()
	{
		return _applicationChannel;
	}

	public void UserId(String userId)
	{
		_userId = userId;
	}

	public String UserId()
	{
		return _userId;
	}

	public void PostId(String postURL)
	{
		_postURL = postURL;
	}

	public String PostURL()
	{
		return _postURL;
	}

	public void LocationSource(String locationSource)
	{

		_locationSource = locationSource;
	}

	public String LocationSource()
	{
		return _locationSource;
	}

	public void FindLocation(boolean findLocation)
	{
		_findLocation = findLocation;
	}

	public boolean FindLocation()
	{
		return _findLocation;
	}

	public BangoLogger()
	{
		_bangoStores = InitStore();
	}

	/**
	 * Loads the persistent store object key then loads the persistent object
	 * itself.
	 *
	 * @return The persistent store object.
	 */
	private BangoStores InitStore()
	{
		_store = PersistentStore.getPersistentObject(BangoConstants.STORE_KEY);
		return LoadStore();
	}

	/**
	 * Saves the persistent store. This should be called when exiting the
	 * application, and is normally done with the onEndSession call.
	 */
	public void SaveStore()
	{
		synchronized (_store)
		{

			try
			{
				_bangoStores.Store().addElement(new BangoStore(_bangoProfile, _bangoEvents));
				_store.setContents(_bangoStores);
				_store.commit();
			}
			catch (ControlledAccessException ex)
			{
			}
		}
	}

	/**
	 * Loads the persistent store object. If one doesn't exist then a new object
	 * is created.
	 *
	 * @return The persistent store object.
	 */
	private BangoStores LoadStore()
	{
		BangoStores returnValue;
		synchronized (_store)
		{
			returnValue = (BangoStores) _store.getContents();
			if (returnValue == null)
			{
				returnValue = new BangoStores();
			}
		}
		return returnValue;
	}

	/**
	 * Initialises the logging system. This should be called when starting the
	 * application, and is normally done with the onStartSession call.
	 *
	 * @param userAgent
	 *            The user agent of the device.
	 * @param applicationId
	 *            The application Id of the application. This should be setup in
	 *            the Bango Management Tools prior to use.
	 */
	public void InitLogger(String userAgent, String applicationId)
	{
		String locationSource = "";
		double locationLatitude = 0;
		double locationLongitude = 0;
		float locationAltitude = 0;
		float locationAccuracy = 0;

		String networkName = BangoConstants.UNKNOWN_INFORMATION;
		String networkCountry = BangoConstants.UNKNOWN_INFORMATION;
		String networkId = BangoConstants.UNKNOWN_INFORMATION;
		int networkType = -1;

		int numberOfNetworks;
		int currentNetworkIndex;

		numberOfNetworks = RadioInfo.getNumberOfNetworks();
		if (numberOfNetworks > 0)
		{
			currentNetworkIndex = RadioInfo.getCurrentNetworkIndex();
			networkName = RadioInfo.getCurrentNetworkName();
			networkCountry = RadioInfo.getNetworkCountryCode(currentNetworkIndex);
			networkId = Integer.toString(RadioInfo.getNetworkId(currentNetworkIndex));
			networkType = RadioInfo.getNetworkType();
		}

		_applicationId = applicationId;
		_userAgent = userAgent;

		if (_applicationVersion == null)
		{
			_applicationVersion = GetApplicationVersion();
		}

		if (_userId == null)
		{
			_userId = Integer.toString(DeviceInfo.getDeviceId());
		}

		Location deviceLocation = GetLocation();

		if (deviceLocation != null && deviceLocation.isValid())
		{
			if (_locationSource == BangoConstants.LOCATION_CELL)
			{

				locationSource = BangoConstants.LOCATION_CELL;
			}
			else if (_locationSource == BangoConstants.LOCATION_ASSISTED)
			{
				locationSource = BangoConstants.LOCATION_ASSISTED;
			}
			else
			{
				locationSource = BangoConstants.LOCATION_GPS;

			}
			locationAccuracy = 0;
			locationLatitude = deviceLocation.getQualifiedCoordinates().getLatitude();
			locationLongitude = deviceLocation.getQualifiedCoordinates().getLongitude();
			if (!Float.isNaN(deviceLocation.getQualifiedCoordinates().getAltitude()))
			{
				locationAltitude = deviceLocation.getQualifiedCoordinates().getAltitude();
			}
		}

		_bangoProfile = new BangoProfile(BangoConstants.SDK_TYPE, BangoConstants.SDK_VERSION, _applicationId, _applicationType, _applicationChannel, _applicationVersion, _userId, _sessionResumePeriod, ConnectionType(), IsRoaming(), locationSource, locationAltitude, locationLatitude, locationLongitude, locationAccuracy, networkId, networkName, networkCountry, networkName, BangoConstants.DEVICE_BUILD_BRAND, BangoConstants.DEVICE_BUILD_DEVICE, BangoConstants.DEVICE_BUILD_MODEL, BangoConstants.DEVICE_BUILD_PRODUCT, BangoConstants.DEVICE_BUILD_VERSION, BangoConstants.DEVICE_BUILD_ID, networkType);
		_bangoEvents = new Vector();
	}

	/**
	 * Posts any events in the persistent store.
	 *
	 * @return Whether or not at least 1 profile and associated events were
	 *         posted successfully.
	 */
	public synchronized boolean PostStoredEvents()
	{
		boolean returnValue = false;
		BangoStore bangoStore;
		int bangoStoresCounter = 0;
		Vector eventsToPost = new Vector();

		while (bangoStoresCounter < _bangoStores.Store().size())
		{
			bangoStore = (BangoStore) _bangoStores.Store().elementAt(bangoStoresCounter);
			eventsToPost.removeAllElements();
			for (int appEventCounter = 0; appEventCounter < bangoStore.BangoEvents().size(); appEventCounter++)
			{
				if (!((BangoEvent) bangoStore.BangoEvents().elementAt(appEventCounter)).EventPosted())
				{
					eventsToPost.addElement(((BangoEvent) bangoStore.BangoEvents().elementAt(appEventCounter)));
				}
			}

			if (eventsToPost.size() == 0 || PostEvents(bangoStore.BangoProfile(), eventsToPost))
			{
				_bangoStores.Store().removeElementAt(bangoStoresCounter);
				returnValue = true;
			}
			else
			{
				bangoStoresCounter++;
			}


		}
		return returnValue;
	}

	/**
	 * Posts the current profile and associated events.
	 *
	 * @return Whether or not the profile and associated events were posted
	 *         successfully.
	 */
	public synchronized boolean PostEvents()
	{

		boolean returnValue = false;
		Vector eventsToPost;
		if (_bangoEvents.size() > 0)
		{
			eventsToPost = new Vector();
			for (int appEventCounter = 0; appEventCounter < _bangoEvents.size(); appEventCounter++)
			{
				if (!((BangoEvent) _bangoEvents.elementAt(appEventCounter)).EventPosted())
				{
					eventsToPost.addElement(((BangoEvent) _bangoEvents.elementAt(appEventCounter)));
				}
			}

			if (eventsToPost.size() > 0)
			{
				returnValue = PostEvents(_bangoProfile, eventsToPost);
				if (returnValue)
				{
					// Set all events as posted
					for (int appEventCounter = 0; appEventCounter < eventsToPost.size(); appEventCounter++)
					{
						((BangoEvent) eventsToPost.elementAt(appEventCounter)).EventPosted(true);
					}
				}
			}
			else
			{
				returnValue = true;
			}
		}
		else
		{
			returnValue = true;
		}
		return returnValue;
	}

	/**
	 * Posts the supplied profile and associated events to the specified url.
	 *
	 * @param bangoProfile
	 *            The profile to post to the specified url.
	 * @param appEvents
	 *            The associated events to post the specified url.
	 * @return Whether or not the profile and associated events were posted
	 *         successfully.
	 */
	private synchronized boolean PostEvents(BangoProfile bangoProfile, Vector bangoEvents)
	{
		boolean returnValue = false;

		String responseMessage;
		int responseCode;

		JSONObject postJSONObject;
		JSONArray postEvents;

		StreamConnection httpPost = null;
		OutputStream outputStream = null;
		InputStream inputStream = null;

		int inputInt;
		String portNumber;
		int hostNameStart;
		int urlPathStart;
		String hostName;
		String urlPath;

		StringBuffer headerBuffer;
		ByteArrayInputStream byteOutputStream = null;
		byte[] outputBuffer = new byte[1024];

		String connectionToUse = BangoStatic.getConnectionToUse();

		try
		{
			if (_logEvents && ((0 == (RadioInfo.getNetworkService() & RadioInfo.NETWORK_SERVICE_ROAMING)) || IsWifiConnection()))
			{
				if (_postURL.trim().toLowerCase().startsWith("http"))
				{
					portNumber = ":" + 80;
				}
				else
				{
					portNumber = ":" + 443;
				}

				hostNameStart = _postURL.indexOf("//") + 2;
				urlPathStart = _postURL.indexOf('/', hostNameStart);

				if (urlPathStart > 0)
				{
					urlPath = _postURL.substring(urlPathStart);
					hostName = _postURL.substring(hostNameStart, urlPathStart);
				}
				else
				{
					hostName = _postURL.substring(hostNameStart);
					urlPath = "/";
				}

				postJSONObject = bangoProfile.GetDeviceProfile();
				postEvents = new JSONArray();
				for (int appEventCounter = 0; appEventCounter < bangoEvents.size(); appEventCounter++)
				{
					postEvents.put(((BangoEvent) bangoEvents.elementAt(appEventCounter)).GetEventDetails());
				}
				postJSONObject.put("Events", postEvents);
				httpPost = (StreamConnection) Connector.open("socket://" + hostName + portNumber + connectionToUse);

				headerBuffer = new StringBuffer();

				headerBuffer.append("POST " + urlPath + " HTTP/1.1\r\n");
				headerBuffer.append("Host: " + hostName + "\r\n");
				if (_userAgent != null && _userAgent != "")
				{
					headerBuffer.append(HttpProtocolConstants.HEADER_USER_AGENT + ": " + _userAgent + "\r\n");
				}
				headerBuffer.append(HttpProtocolConstants.HEADER_CONTENT_TYPE + ": application/json\r\n");
				headerBuffer.append(HttpProtocolConstants.HEADER_CONTENT_LENGTH + ": " + postJSONObject.toString().getBytes().length + "\r\n");
				headerBuffer.append(HttpProtocolConstants.HEADER_CONNECTION + ": Close\r\n");
				headerBuffer.append("\r\n");

				outputStream = httpPost.openOutputStream();
				inputStream = httpPost.openInputStream();

				outputStream.write(headerBuffer.toString().getBytes());
				outputStream.flush();

				byteOutputStream = new ByteArrayInputStream(postJSONObject.toString().getBytes());

				while ((inputInt = byteOutputStream.read(outputBuffer)) > 0)
				{
					outputStream.write(outputBuffer);
					outputStream.flush();
				}

				outputStream.flush();
				outputStream.close();

				responseMessage = "";

				while ((inputInt = inputStream.read()) != -1)
				{
					responseMessage += ((char) inputInt);
				}
				if (responseMessage.length() > 12)
				{
					responseCode = Integer.parseInt(responseMessage.substring(9, 9 + 3));
					if (responseCode == HttpConnection.HTTP_OK)
					{
						inputInt = responseMessage.indexOf("\r\n\r\n");
						if (inputInt > -1)
						{
							responseMessage = responseMessage.substring(inputInt + 4);
						}
						else
						{
							responseMessage = "";
						}
					}
					else
					{
						responseMessage = "";
					}
				}
				else
				{
					responseMessage = "";
				}

				returnValue = responseMessage.startsWith(BangoConstants.POST_RESPONSE_SUCCESS);

			}

		}
		catch (Exception ex)
		{
			returnValue = false;
		}
		finally
		{
			try
			{

				if (byteOutputStream != null)
				{
					byteOutputStream.close();
				}

				if (outputStream != null)
				{
					outputStream.close();
				}

				if (inputStream != null)
				{
					inputStream.close();
				}

				if (httpPost != null)
				{
					httpPost.close();
				}
			}
			catch (IOException e)
			{

			}
		}

		return returnValue;

	}

	public String viewBatch()
	{
		JSONObject postJSONObject;
		JSONArray postEvents;

		postJSONObject = _bangoProfile.GetDeviceProfile();
		postEvents = new JSONArray();
		for (int appEventCounter = 0; appEventCounter < _bangoEvents.size(); appEventCounter++)
		{
			postEvents.put(((BangoEvent) _bangoEvents.elementAt(appEventCounter)).GetEventDetails());
		}

		try {
			postJSONObject.put("Events", postEvents);
		} catch (Exception e) {
		}

		return postJSONObject.toString();
	}


	/**
	 * Gets the application version from the application configuration.
	 *
	 * @return The application version.
	 */
	private String GetApplicationVersion()
	{
		String returnValue = BangoConstants.UNKNOWN_INFORMATION;
		try
		{
			returnValue = ApplicationDescriptor.currentApplicationDescriptor().getVersion();
		}
		catch (Exception ex)
		{
			returnValue = BangoConstants.UNKNOWN_INFORMATION;
			LogInternalErrorWarning(ex);
		}
		return returnValue;
	}

	/**
	 * Gets the connection type. If the connection is a WiFi connection this
	 * will be returned. Otherwise the operator connection type will be
	 * returned.
	 *
	 * @return The connection type in integer value. This is converted to a
	 *         textual value on posting.
	 */
	private int ConnectionType()
	{
		if (IsWifiConnection())
		{
			return BangoConstants.WIFI_CONNECTION;
		}
		else
		{
			return RadioInfo.getNetworkType();
		}
	}

	/**
	 * Gets whether or not the user is currently roaming.
	 *
	 * @return Whether or not the user is currently roaming.
	 */
	private boolean IsRoaming()
	{
		return (0 != (RadioInfo.getNetworkService() & RadioInfo.NETWORK_SERVICE_ROAMING));
	}

	/**
	 * Gets whether or not the user is currently using a WiFi connection.
	 *
	 * @return Whether or not the user is currently using a WiFi connection.
	 */
	private boolean IsWifiConnection()
	{
		return (WLANInfo.getWLANState() == WLANInfo.WLAN_STATE_CONNECTED) && RadioInfo.areWAFsSupported(RadioInfo.WAF_WLAN);
	}

	/**
	 * Logs an event to the internal logging engine.
	 *
	 * @param eventName
	 *            The name of the event to log.
	 * @param errorLevel
	 *            The error level of the event to log.
	 * @param eventValue
	 *            The value of the event to log.
	 * @param eventDetail
	 *            The detail of the event to log.
	 * @param errorId
	 *            The error Id of the event to log.
	 * @param eventParameters
	 *            Any custom parameters to log with events.
	 * @return the internal id of the event.
	 */
	private int LogEvent(String eventName, int errorLevel, String eventValue, String eventDetail, String errorId, Hashtable eventParameters)
	{
		int returnValue = -1;
		Date eventTime = BangoStatic.GetUTCDate();
		Date eventLocalTime = new Date();
		Vector eventParametersVector = null;
		String eventParameterName;
		if (eventParameters != null)
		{
			eventParametersVector = new Vector();
			for (Enumeration eventParameterEnumerator = eventParameters.keys(); eventParameterEnumerator.hasMoreElements();)
			{
				eventParameterName = (String) eventParameterEnumerator.nextElement();
				eventParametersVector.addElement(new BangoNameValuePair(eventParameterName, eventParameters.get(eventParameterName).toString()));
			}

		}

		if (_logEvents)
		{
			_bangoEvents.addElement(new BangoEvent(_bangoEvents.size(), ConnectionType(), RadioInfo.getNetworkType(), errorLevel, eventName, eventValue, eventDetail, errorId, eventTime, eventLocalTime, eventParametersVector));
			returnValue = _bangoEvents.size();
		}
		return returnValue;
	}

	/**
	 * Logs an event to the internal logging engine.
	 *
	 * @param eventName
	 *            The name of the event to log.
	 * @param eventValue
	 *            The value of the event to log.
	 * @param eventDetail
	 *            The detail of the event to log.
	 * @param eventParameters
	 *            Any custom parameters to log with events.
	 * @return The internal id of the event.
	 */
	public int LogEvent(String eventName, String eventValue, String eventDetail, Hashtable eventParameters)
	{
		return LogEvent(eventName, BangoConstants.ERROR_NONE, eventValue, eventDetail, "", eventParameters);
	}

	/**
	 * Logs an the session start event to the internal logging engine.
	 *
	 * @param eventDetail
	 *            The detail of the event to log.
	 * @param eventValue
	 *            The value of the event to log.
	 * @param eventParameters
	 *            Any custom parameters to log with events.
	 * @return The internal id of the event.
	 */
	public int LogEventStart(String eventValue, String eventDetail, Hashtable eventParameters)
	{
		return LogEvent(BangoSessionEvents.SESSION_START, eventValue, eventDetail, eventParameters);
	}

	/**
	 * Logs an the session end event to the internal logging engine.
	 *
	 * @param eventDetail
	 *            The detail of the event to log.
	 * @param eventValue
	 *            The value of the event to log.
	 * @param eventParameters
	 *            Any custom parameters to log with events.
	 * @return The internal id of the event.
	 */
	public int LogEventEnd(String eventValue, String eventDetail, Hashtable eventParameters)
	{
		return LogEvent(BangoSessionEvents.SESSION_END, eventValue, eventDetail, eventParameters);
	}

	/**
	 * Logs an error event to the internal logging engine.
	 *
	 * @param errorId
	 *            The error Id of the event to log.
	 * @param message
	 *            The error message.
	 * @param errorClass
	 *            The class in which the error occurred.
	 * @param eventParameters
	 *            Any custom parameters to log with events.
	 * @return The internal id of the error.
	 */
	public int LogEventError(String errorId, String message, String errorClass, Hashtable eventParameters)
	{
		return LogEvent(BangoSessionEvents.EVENT_ERROR, BangoConstants.ERROR_USER, errorClass, message, errorId, eventParameters);
	}

	/**
	 * Logs a critical internal error event to the internal logging engine.
	 *
	 * @param ex
	 *            The error that was raised.
	 * @return The internal id of the error.
	 */
	public int LogInternalErrorCritical(Exception ex)
	{
		return LogInternalError(ex, BangoConstants.ERROR_INTERNAL_CRITICAL);
	}

	/**
	 * Logs a warning internal error event to the internal logging engine.
	 *
	 * @param ex
	 *            The error that was raised.
	 * @return The internal id of the error.
	 */
	public int LogInternalErrorWarning(Exception ex)
	{
		return LogInternalError(ex, BangoConstants.ERROR_INTERNAL_WARNING);
	}

	/**
	 * Logs an internal error event to the internal logging engine.
	 *
	 * @param ex
	 *            The error that was raised.
	 * @param errorLevel
	 *            The error level of the error (Warning/Critical)
	 * @return The internal id of the error.
	 */
	private int LogInternalError(Exception ex, int errorLevel)
	{
		return LogEvent("", errorLevel, ex.toString(), ex.getMessage(), "", null);
	}

	/**
	 * Initialises the chosen location provider.
	 *
	 * @return The last known location. If this is not known known null is
	 *         returned.
	 */
	private Location GetLocation()
	{
		Location returnValue = null;
		Criteria criteria;
		if (_findLocation)
		{
			criteria = new Criteria();

			if (_locationSource == BangoConstants.LOCATION_CELL)
			{
				criteria.setPreferredPowerConsumption(Criteria.POWER_USAGE_LOW);
				criteria.setHorizontalAccuracy(Criteria.NO_REQUIREMENT);
				criteria.setVerticalAccuracy(Criteria.NO_REQUIREMENT);
				criteria.setCostAllowed(true);
			}
			else if (_locationSource == BangoConstants.LOCATION_ASSISTED)
			{
				criteria.setPreferredPowerConsumption(Criteria.POWER_USAGE_MEDIUM);
				criteria.setHorizontalAccuracy(500);
				criteria.setVerticalAccuracy(500);
				criteria.setCostAllowed(true);
			}
			else
			{
				criteria = new Criteria();
				criteria.setPreferredPowerConsumption(Criteria.POWER_USAGE_HIGH);
				criteria.setHorizontalAccuracy(2000);
				criteria.setVerticalAccuracy(2000);
				criteria.setCostAllowed(false);
			}

			try
			{
				_locationProvider = LocationProvider.getInstance(criteria);
			}
			catch (LocationException ex)
			{
				_findLocation = false;
			}

			if (_locationProvider != null)
			{
				try
				{
					returnValue = LocationProvider.getLastKnownLocation();
					_locationProvider.setLocationListener(this, 30, 30, -1);
				}
				catch (Exception ex)
				{
					_findLocation = false;
				}
			}
		}
		return returnValue;
	}

	/**
	 * Destroys the location finder.
	 */
	public void EndLocationUpdates()
	{

		if (_findLocation && _locationProvider != null)
		{
			_locationProvider.reset();
			_locationProvider.setLocationListener(null, -1, -1, -1);
		}
	}

	/**
	 * Updates the profile with the new location.
	 *
	 * @param location
	 *            The new location of the user.
	 */
	private void UpdateLocation(Location location)
	{
		if (location.isValid())
		{
			float altitude = 0;
			if (!Float.isNaN(location.getQualifiedCoordinates().getAltitude()))
			{
				altitude = location.getQualifiedCoordinates().getAltitude();
			}

			EndLocationUpdates();

			_bangoProfile.LocationLatitude(location.getQualifiedCoordinates().getLatitude());
			_bangoProfile.LocationLongitude(location.getQualifiedCoordinates().getLongitude());
			_bangoProfile.LocationAccuracy(0);
			_bangoProfile.LocationAltitude(altitude);
		}
	}

	/**
	 * Called when the location provider has been updated.
	 *
	 * @param provider
	 *            The provider used to obtain the latest location.
	 * @param location
	 *            The new location of the user.
	 */
	public void locationUpdated(LocationProvider provider, Location location)
	{
		UpdateLocation(location);
	}

	/**
	 * Called by the LocationProvider to which this listener is registered if
	 * the state of the LocationProvider has changed.
	 *
	 * @param provider
	 *            The provider used to obtain the latest location.
	 * @param newState
	 *            The new state of the LocationProvider. This value is one of
	 *            the constants for the state defined in the LocationProvider
	 *            class.
	 */
	public void providerStateChanged(LocationProvider provider, int newState)
	{

	}
}
